/*
 * Copyright (c) 2011 Imre Fazekas.
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * Neither the name of the Brillien nor the names of its
 * terms and concepts may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package com.vii.streamline.services.reflection;

import com.vii.streamline.annotation.P;
import com.vii.streamline.services.OperationServices;
import com.vii.streamline.services.StringServices;
import com.vii.streamline.services.TypeServices;
import com.vii.streamline.services.error.StreamLineException;
import com.vii.streamline.structures.collections.InnerList;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;

/**
 * Service class to handle javascript-based operations
 */
public class ReflectionServices {

    private static Object convertToArray( List list, Class primitiveType ) throws InvocationTargetException, IllegalAccessException {
        String resultTypeName = primitiveType.getComponentType().getSimpleName();
        Method converter = null;
        try {
            converter = getMethod(
                    TypeServices.class,
                    "convertTo" + resultTypeName.substring(0,1).toUpperCase() + resultTypeName.substring(1) ,
                    list.get(0).getClass()
            );
        } catch (NoSuchMethodException e) {
            e.printStackTrace();  
        }


        Object array = Array.newInstance( primitiveType.getComponentType(), list.size() );
        for( int i = 0; i<list.size(); ++i )
            Array.set( array, i,
                    converter == null
                    ? list.get(i)
                    : converter.invoke( TypeServices.class, list.get(i) )
            );
        return array;
    }

    /**
     * Gets the list of annotated elements narrowed by the given annotation type 
     * @param a annotation type 
     * @param elements elements to be narrowed
     * @param <T> Generic type of annotated elements
     * @param <P> Generic type of the Annotation
     * @return list of selected annotated elements
     */
    public static <T extends AnnotatedElement, P extends Annotation> Collection<T> getAnnotatedElements( Class<P> a, T... elements ){
        ArrayList<T> list = new ArrayList<T>();
        for( AnnotatedElement element : elements ){

            //ApplicationServices.systemLog( Level.FINEST, "element::", element.getClass().getName(), element.toString() );

            for( Annotation ae : element.getAnnotations() )
                if( a.isInstance( ae ) )
                    list.add( (T)element );
        }
        return list;
    }

    /**
     * By passing a collection of field containing all defined and inherited field, this method removes the inherited ones if they are overridden by the given "relatedClass"
     * @param fields collection of fields
     * @param relatedClass owner of the fields
     * @return narrowed collection of fields
     */
    public static Collection<Field> removeHidedFields( Collection<Field> fields, Class relatedClass ){
        InnerList<Field> result = new InnerList<Field>();
        for( Field field : fields ){
            if( !field.getDeclaringClass().equals( relatedClass ) ){
                boolean needed = true;
                for( Field iField : fields ){
                    if(
                            !iField.equals( field ) && iField.getName().equals( field.getName() ) &&
                            field.getDeclaringClass().isAssignableFrom( iField.getDeclaringClass() )
                    ){
                        needed = false; break;
                    }
                }
                if( needed )
                    result.add( field );
            }
            else result.add( field );
        }
        return result;
    }


    /**
     * Gets the list of annotated fields narrowed by the given annotation type. Calls the getAnnotatedElements method
     */
    public static <T extends AnnotatedElement, P extends Annotation> Collection<T> getAnnotatedFields( Class<P> a, Class c ){
        return (Collection<T>) getAnnotatedElements( a, c.getFields() );
    }

    /**
     * Gets the list of annotated declared fields narrowed by the given annotation type. Calls the getAnnotatedElements method
     */
    public static <T extends AnnotatedElement, P extends Annotation> Collection<T> getAnnotatedDeclaredFields( Class<P> a, Class c ){
        return (Collection<T>) getAnnotatedElements( a, c.getDeclaredFields() );
    }

    /**
     * Gets the list of annotated methods narrowed by the given annotation type. Calls the getAnnotatedElements method   
     */
    public static <T extends AnnotatedElement, P extends Annotation> Collection<T> getAnnotatedMethods( Class<P> a, Class c ){
        return (Collection<T>) getAnnotatedElements( a, c.getDeclaredMethods() );
    }

    /**
     * Getter method to access the fields of the class c 
     */
    public static <T extends AnnotatedElement> Collection<T> getFields( Class c ){
        return (Collection<T>) new InnerList( c.getFields() );
    }

    /**
     * Getter method to access the declared fields of the class c 
     */
    public static <T extends AnnotatedElement> Collection<T> getDeclaredFields( Class c ){
        return (Collection<T>) new InnerList( c.getDeclaredFields() );
    }

    /**
     * Decides whether the methode with name "name" exists in class "c" or not
     */
    public static boolean isMethodExist( Class c, String name ) {
        InnerList<Method> list = new InnerList<Method>( c.getDeclaredMethods(), c.getMethods() );
        for( Method m : list )
            if( m.getName().equals( name ) )
                return true;
        return false;
    }
    
    public static Class[] getTypes( Serializable... types ){
        Class[] classes = new Class[ types.length ];
        for( int i=0; i<types.length; i++ )
            classes[ i ] = types[ i ] == null ? Object.class : types[ i ].getClass();
        return classes;
    }

    /**
     * Getter method to access the first matchnig method of the class c with the given name 
     */
    public static Method getMethod( Class c, String name ) throws NoSuchMethodException {
        InnerList<Method> list = new InnerList<Method>( c.getDeclaredMethods(), c.getMethods() );
        for( Method m : list ){
            if( m.getName().equals( name ) ){
                m.setAccessible( true );
                return m;
            }
        }
        return null;
    }

    private static boolean isConvenientMethod( String methodName, Method m, boolean convertingEnabled, Class... parameters ){
        if( m.getName().equals( methodName ) ){
            Class[] parameterTypes = m.getParameterTypes();

            if( parameters.length != parameterTypes.length )
                return false;

            boolean equals = true;
            for( int i=0; equals && i<parameters.length; ++i ){
                equals =    m.getParameterTypes()[i].isAssignableFrom( parameters[i] ) ||
                            (
                                convertingEnabled &&
                                List.class.isAssignableFrom( parameters[i] ) &&
                                parameterTypes[i].isArray()
                            );
            }

            if( equals ){
                return true;
            }
        }
        return false;
    }

    /**
     * Getter method to access the first matchnig method of the class c with the given name and parametertypes
     */
    public static Method getMethod( Class c, String name, Class... parameters ) throws NoSuchMethodException {

        InnerList<Method> list = new InnerList<Method>( c.getDeclaredMethods(), c.getMethods() );
        for( Method m : list ){
            if( isConvenientMethod( name, m, false, parameters ) ){
                m.setAccessible( true );
                return m;
            }
        }

        return null;
    }

    /**
     * Retrieves all available constructor in the given class
     */
    public static <T> List<Constructor<T>> getConstructors( Class<T> c ){
        InnerList<Constructor<T>> list = new InnerList<Constructor<T>>(
                (Constructor<T>[])c.getConstructors(), (Constructor<T>[])c.getDeclaredConstructors()
        );

        return list;
    }

    /**
     * Instantiates a class
     */
    public static <T> T newInstance(Class<T> tc ) throws InvocationTargetException, IllegalAccessException, InstantiationException {
        if( tc.isMemberClass() && !Modifier.isStatic( tc.getModifiers() ) )
            throw new InstantiationException( "To instantiate an inner class, an instance of the outer class is needed!" );

        return tc.newInstance();
    }

    /**
     * Instantiates an inner class by an instance of the outer class
     */
    public static <T> T newInstance(Class<T> tc, Object outerClassInstance ) throws InvocationTargetException, IllegalAccessException, InstantiationException {
        if( !tc.isMemberClass() || Modifier.isStatic( tc.getModifiers() ) || outerClassInstance == null )
            return ReflectionServices.<T>newInstance( tc );

        List<Constructor<T>> constructors = ReflectionServices.<T>getConstructors(tc);
        for( Constructor<T> c : constructors )
            if( c.getParameterTypes().length == 1 && c.getParameterTypes()[0].isAssignableFrom( outerClassInstance.getClass() ) )
                return c.newInstance( outerClassInstance );

        throw new InstantiationException( "No proper constructor found!" );
    }


    /**
     * Tells whether a typelist ends with Array foreshadowign varargs in present
     */
    public static boolean isVarargs( Class[] types ){
        return types != null && types.length>0 && types[ types.length -1 ].isArray();
    }

    /**
     * Rearrange parameters according to the given formal parameter types
     */
    public static Object[] conformiseParameters( Class[] types, Object... parameters ){
        if( !isVarargs(types) )
            return parameters;

        Object[] os = new Object[ types.length ];
        for( int i=0; i<types.length-1; ++i ){
            os[i] = parameters[i];
        }
        Object[] array = (Object[])Array.newInstance( types[types.length-1].getComponentType(), parameters.length-types.length+1 );
        for( int i=0; i<array.length; ++i ){
            array[i] = parameters[ types.length-1 + i ];
        }
        os[types.length-1] = array;

        return os;
    }

    /**
     * Invoke a method of an object using the given parameters
     */
    public static <T> T simpleInvokeMethod( Method m, Object owner, Object[] parameters ) throws InvocationTargetException, IllegalAccessException {
        m.setAccessible(true);
        return (T) m.invoke( owner, parameters );
    }

    /**
     * Invoke a method of an object using the given parameters
     */
    public static <T> T invokeMethodALike( String methodName, Object owner, Object... parameters ) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, StreamLineException {
        Class c = owner instanceof Class ? (Class)owner : owner.getClass();

        Method m = getMethod( c, methodName );

        if( m == null )
            throw new NoSuchMethodException( "No matching found:" + methodName );

        return ReflectionServices.<T>invokeMethodALike(m, owner, parameters);
    }

    /**
     * Invoke a method of an object using the given parameters
     */
    public static <T> T invokeMethodALike( Method m, Object owner, Object... parameters ) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, StreamLineException {
        if( m == null )
            throw new IllegalArgumentException("Method reference is null!");

        m.setAccessible( true );

        parameters = conformiseParameters( m.getParameterTypes(), parameters );

        Object[] realParameters = new Object[ m.getParameterTypes().length ];
        for( int i=0; i<realParameters.length; ++i ){
            realParameters[i] =
                TypeServices.convert( parameters[i], m.getParameterTypes()[i] );
        }

        if( ReflectionServices.isParameterListAnnotated(m) ){
            List<P> annotations = ReflectionServices.getFormalParameterAnnotations(m);
            for( int i=0; i<realParameters.length; ++i ){
                // custom type conversion
                if( realParameters[i] != null ){
                    if( HashMap.class.isAssignableFrom( realParameters[i].getClass() ) && !m.getParameterTypes()[i].getPackage().getName().startsWith("java.") ){
                        realParameters[i] = TypeServices.fillObject( (HashMap)realParameters[i], m.getParameterTypes()[i] );
                    }
                    // List of custom type conversion
                    else if( List.class.isAssignableFrom( realParameters[i].getClass() ) && !annotations.get(i).collectedType().equals(Object.class) ){
                        List l = (List)realParameters[i];
                        List res = new LinkedList();
                        for( Object o : l ){
                            if( HashMap.class.isAssignableFrom( o.getClass() ) ){
                                res.add( TypeServices.fillObject( (HashMap)o, annotations.get(i).collectedType() ) );
                            } else res.add( o );
                        }
                        realParameters[i] = res;
                    }
                    // HashMap of custom type conversion
                    else if( HashMap.class.isAssignableFrom( realParameters[i].getClass() ) && (!annotations.get(i).collectedType().equals(Object.class) || !annotations.get(i).keyType().equals(Object.class) ) ){
                        HashMap h = (HashMap)realParameters[i];
                        boolean customKey = !annotations.get(i).keyType().equals(Object.class);
                        boolean customValue = !annotations.get(i).collectedType().equals(Object.class);
                        for( Object o : h.keySet() ){
                            Object key = o, value = h.get(o);
                            h.remove( key );
                            if( customKey && HashMap.class.isAssignableFrom( key.getClass() ) ){
                                key = TypeServices.fillObject( (HashMap)key, annotations.get(i).keyType() );
                            }
                            if( customValue && HashMap.class.isAssignableFrom( value.getClass() ) ){
                                value = TypeServices.fillObject( (HashMap)value, annotations.get(i).collectedType() );
                            }
                            h.put( key, value );
                        }
                    }
                }
            }
        }

        return ReflectionServices.<T>invokeMethod( m, owner, realParameters );
    }

    /**
     * Invoke a method of an object using the given types and parameters
     */   /*
    public static <T> T invokeMethod( String methodName, Object owner, Class[] types, Object... parameters ) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        Method m = getMethod( owner instanceof Class ? (Class)owner : owner.getClass(), methodName, types );
        return ReflectionServices.<T>invokeMethod( m, owner, parameters );
    }  */

    /**
     * Invoke a method of an object using the given parameters
     */
    public static <T> T invokeMethod( String methodName, Object owner, Object... parameters ) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, StreamLineException {
        Method m = getMethod( owner instanceof Class ? (Class)owner : owner.getClass(), methodName );
        /*
        Class[] types = new Class[ parameters.length ];
        for( int i=0; i<parameters.length; ++i )
            types[i] = parameters[i] == null
                    ? (_m != null && _m.getParameterTypes().length > i ? _m.getParameterTypes()[i] : Object.class)
                    : parameters[i].getClass();

        Method m = getMethod( owner instanceof Class ? (Class)owner : owner.getClass(), methodName, types );
        */
        return ReflectionServices.<T>invokeMethod( m, owner, parameters );
    }

    /**
     * Invoke a method of an object using the given parameters
     */
    public static <T> T invokeMethod( Method m, Object owner, Object... parameters ) throws InvocationTargetException, IllegalAccessException {
        if( m == null ){
            throw new IllegalArgumentException("Method reference is null!");
        }

        m.setAccessible(true);

        return  m.getParameterTypes().length == 0
                ? (T)m.invoke( owner )
                : (T)m.invoke( owner, parameters );
    }

    /**
     * Getter method to access the value of a field by invoking the get method associated to the field
     */
    public static <T> T getFieldValueThroughGetMethod( Class c, Object owner, String fieldName ) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        return (T)invokeMethod(
                c.getMethod( "get" + Character.toUpperCase( fieldName.charAt(0) ) + fieldName.substring(1) ),
                owner
        );
    }

    /**
     * Setter method to access the value of a field by invoking the get method associated to the field
     */
    public static <T> void setFieldValue( Class c, Object owner, String fieldName, T value ) throws NoSuchFieldException, IllegalAccessException, StreamLineException {
        Collection<Field> fields = new InnerList<Field>(
                ReflectionServices.<Field>getDeclaredFields( c ),
                ReflectionServices.<Field>getFields( c )
        );
        for( Field f : fields )
            if( f.getName().equals( fieldName ) ){
                f.setAccessible(true);
                f.set( owner, TypeServices.convert(value, f.getType()) );
                return;
            }
        throw new StreamLineException( "Cannot found referred field!" ); 
    }

    /**
     * Getter method to access the value of a field by invoking the get method associated to the field
     */
    public static <T> T setFieldValueThroughSetMethod( Class c, Object owner, String fieldName, Object value ) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, StreamLineException {
        Method m = getMethod( c, "set" + Character.toUpperCase( fieldName.charAt(0) ) + fieldName.substring(1) );
        if( m == null || m.getParameterTypes().length != 1 )
            throw new StreamLineException( "No set method found for field: " + fieldName ); 
        return (T)invokeMethod(
                m, owner, TypeServices.convert(value, m.getParameterTypes()[0] )
        );
    }

    /**
     * Getter method to access the value of a field 
     */
    public static <T> T getFieldValue( Class c, Object owner, String fieldName ) throws NoSuchFieldException, IllegalAccessException {
        Field f = c.getField( fieldName );
        f.setAccessible(true);
        return (T)f.get( owner );
    }


    public static boolean isParameterListAnnotated( Method m ) throws StreamLineException {
        try {
            if( m == null )
                throw new StreamLineException("MessageProcessor does not exist!");

            if( m.getParameterTypes().length == 0 )
                return true;

            Annotation[][] pass = m.getParameterAnnotations();

            for( Annotation[] pas : pass ){
                boolean found = false;
                P annotation = null;
                for( Annotation a : pas ){
                    if( P.class.isAssignableFrom( a.annotationType() ) ){
                        annotation = (P) a; found = true; break;
                    }
                }

                return found;
            }

            return false;
        } catch (Exception e) {
            throw new StreamLineException( e.getMessage() );
        }
    }

    public static List<P> getFormalParameterAnnotations( Method m ) throws StreamLineException {
        try {
            if( m == null )
                throw new StreamLineException("MessageProcessor does not exist!");

            Annotation[][] pass = m.getParameterAnnotations();
            List<P> parameterAnnotations = new LinkedList<P>();

            for( Annotation[] pas : pass ){
                boolean found = false;
                P annotation = null;
                for( Annotation a : pas ){
                    if( P.class.isAssignableFrom( a.annotationType() ) ){
                        annotation = (P) a; found = true; break;
                    }
                }

                if( !found || annotation == null )
                    throw new StreamLineException( "Method parameters are not annotated properly for method: " + m.getName() + "!" );

                parameterAnnotations.add( annotation );
            }

            return parameterAnnotations;
        } catch (Exception e) {
            throw new StreamLineException( e.getMessage() );
        }
    }

    /**
     * Returns the names of the parameters (specified by annotation {@link com.vii.streamline.annotation.P}) of the given {@link Method}.
     */
    public static List<String> getFormalParameterNames( Method m ) throws StreamLineException {
        try {
            if( m == null )
                throw new StreamLineException("MessageProcessor does not exist!");

            Annotation[][] pass = m.getParameterAnnotations();
            List<String> parameterNames = new LinkedList<String>();

            for( Annotation[] pas : pass ){
                boolean found = false;
                P annotation = null;
                for( Annotation a : pas ){
                    if( P.class.isAssignableFrom( a.annotationType() ) ){
                        annotation = (P) a; found = true; break;
                    }
                }

                if( !found || annotation == null )
                    throw new StreamLineException( "Method parameters are not annotated properly for method: " + m.getName() + "!" );

                parameterNames.add( annotation.name() );
            }

            return parameterNames;
        } catch (Exception e) {
            throw new StreamLineException( e.getMessage() );
        }
    }

    /**
     * Returns the types of the parameters of the given {@link Method}.
     */
    public static List<Class> getFormalParameterTypes( Method m ) throws StreamLineException{
        try {
            if( m == null )
                throw new StreamLineException("MessageProcessor does not exist!");

            Class[] classes = m.getParameterTypes();
            List<Class> parameterTypes = new LinkedList<Class>();

            for( Class type : classes )
                parameterTypes.add( type );

            return parameterTypes;
        } catch (Exception e) {
            throw new StreamLineException( e.getMessage() );
        }
    }

    /**
     * Returns the types of the parameters of the given {@link Method}.
     */
    public static Map<String,Class> getFormalParameterMap( Method m ) throws StreamLineException {
        Map<String,Class> res = new LinkedHashMap<String,Class>();

        List<String> formalParameterNames = getFormalParameterNames( m );
        List<Class> formalParameterTypes = getFormalParameterTypes( m );

        for( int i=0; i<formalParameterNames.size(); ++i )
            res.put(
                    formalParameterNames.get(i),
                    formalParameterTypes.get(i)
            );

        return res;
    }

    public static List<Method> getServiceMethods( Class c ) throws StreamLineException {
        return getServiceMethods(c, true);
    }
    public static List<Method> getServiceMethods( Class c, MethodSelector selector ) throws StreamLineException {
        return getServiceMethods(c, true, selector);
    }
    public static List<Method> getServiceMethods( Class c, boolean isMemberFunction ) throws StreamLineException {
        return getServiceMethods(c, isMemberFunction, null);
    }
    public static List<Method> getServiceMethods( Class c, boolean isMemberFunction, MethodSelector selector ) throws StreamLineException {
        List<Method> res = new InnerList<Method>();

        for( Method m : c.getMethods() ){
            m.setAccessible( true );

            int modifiers = m.getModifiers();

            if(
                    !StringServices.startsWith( m.getDeclaringClass().getName(), "java.", "sun.", "sunw.", "com.sun.", "javax." ) &&
                    Modifier.isPublic( modifiers ) && !Modifier.isAbstract(modifiers) &&
                    (isMemberFunction == !Modifier.isStatic(modifiers)) &&
                    ReflectionServices.isParameterListAnnotated( m ) &&
                    (selector == null || selector.isMethodConvenient(m))
            ){
                res.add( m );
            }
        }

        return res;
    }

    public static HashMap<String, ReflectionReference> getReferences( Class... types ) throws StreamLineException {
        HashMap<String, ReflectionReference> references = new HashMap<String, ReflectionReference>();
        for( Class c : types ){
            List<Method> serviceMethods = new InnerList<Method>(
                    getServiceMethods( c ),
                    getServiceMethods( c, false )
            );
            for( Method m : serviceMethods ){
                if( references.containsKey( m.getName() ) ){
                    references.remove( m.getName() );
                } else
                    references.put( m.getName(), new ReflectionReference( c, Modifier.isStatic( m.getModifiers() ) ) );

                references.put( c.getSimpleName() + Invocation.REFERENCE_SEPARATOR + m.getName(),
                        new ReflectionReference( c, Modifier.isStatic( m.getModifiers() ) )
                );
            }
        }
        return references;
    }

    public static HashMap<String, ReflectionReference> getReferences( Object... providers ) throws StreamLineException {
        HashMap<String, ReflectionReference> references = new HashMap<String, ReflectionReference>();
        for( Object provider : providers ){
            Class c = provider.getClass();
            List<Method> serviceMethods = new InnerList<Method>(
                    getServiceMethods( c ),
                    getServiceMethods( c, false )
            );
            for( Method m : serviceMethods ){
                if( references.containsKey( m.getName() ) ){
                    references.remove( m.getName() );
                } else
                    references.put( m.getName(), new ReflectionReference( c, Modifier.isStatic( m.getModifiers() ), provider ) );

                references.put( c.getSimpleName() + Invocation.REFERENCE_SEPARATOR + m.getName(),
                        new ReflectionReference( c, Modifier.isStatic( m.getModifiers() ), provider )
                );
            }
        }
        return references;
    }

    public static Object[] reformulateParameters( List<String> formalParameterNames, List<Class> formalParameterTypes, Map<String, Object> parameters ) throws StreamLineException {
        Object[] os =  new Object[ formalParameterNames.size() ];
        for( int i=0; i<os.length; ++i ){
            if( !OperationServices.containsKey(parameters, formalParameterNames.get(i)) ){
                throw new StreamLineException( "No parameter found to this formal parameter:" + formalParameterNames.get(i) );
            }

            try{
                Object o = OperationServices.get(parameters, formalParameterNames.get(i));

                os[ i ] =   o instanceof HashMap && !formalParameterTypes.get(i).equals( HashMap.class.getName() )
                            ? TypeServices.fillObject((HashMap) o, formalParameterTypes.get(i))
                            : o ;
            } catch( Exception e ){
                throw new StreamLineException("Conversion cannot be made: " + formalParameterNames.get(i) + " " + formalParameterTypes.get(i) );
            }
        }
        return os;
    }

    public static <T> T getData(Object container, String member){
        if( container instanceof Map ){
            Map m = (Map)container;
            Object o;
            if( (o = m.get(member)) != null )
                return (T)o;
        }

        try {
            return (T)getFieldValue( container.getClass(), container, member );
        } catch (Exception e) {  }

        try {
            return (T)getFieldValueThroughGetMethod(container.getClass(), container, member);
        } catch (Exception e) {  }

        throw new IllegalArgumentException("Member \"" + member + "\" cannot found in " + container + ".");
    }

    public static <T> T getEmbeddedData(Object container, String qualifiedPath){
        String[] path = qualifiedPath.split("\\.");
        Object o = container;
        for (String s : path) {
            o = getData( o, s );
        }
        return (T)o;
    }

}
